<template>
  <el-select
    ref="select"
    :value="value"
    :placeholder="placeholder"
    clearable
    :multiple="multiple"
    :disabled="disabled"
    style="width: 100%;text-align:center;"
    @clear="clear"
    @remove-tag="removeTag"
    @visible-change="visibleChange"
  >
  
    <el-option
      ref="option"
      class="tree-select__option"
      v-for="item, idx in selectedData" 
      :key="idx"
      :value="item[nodeKey]"
      :label="item.label"
      style="display: none;"
    />
    <div class="div_search_input">
      <el-input v-if="filterable" v-model="searchVal" class="search_input" size="mini" prefix-icon="el-icon-search"/>
    </div>
    <el-tree
      v-show="!remoteSearch || !searchVal"
      ref="tree"
      class="tree-select__tree"
      :class="`tree-select__tree--${multiple ? 'checked' : 'radio'} isSearch--${filterable ? 'padding' : 'no_padding'}`"
      :node-key="nodeKey"
      :data="data"
      :props="props"
      check-strictly
      :default-expanded-keys="multiple ? (value ? value : []) : [value]"
      :show-checkbox="multiple"
      :highlight-current="!multiple"
      :expand-on-click-node="multiple"
      :filter-node-method="filterNode"
      @node-click="handleNodeClick"
      @check-change="handleCheckChange"
      :lazy="lazy"
      :load="loadOptions"
    >
        <span style="font-size:14px;" :style="node.disabled ? 'color: #a7a7a7;cursor: not-allowed;width:100%;' : ''" slot-scope="{ node, data }">{{ node[props.label] }}</span>
    </el-tree>
    <el-tree
      v-show="remoteSearch && searchVal"
      ref="remoteTree"
      class="tree-select__tree"
      :class="`tree-select__tree--${multiple ? 'checked' : 'radio'} isSearch--${filterable ? 'padding' : 'no_padding'}`"
      :node-key="nodeKey"
      :data="remoteData"
      :props="props"
      check-strictly
      :show-checkbox="multiple"
      :highlight-current="!multiple"
      :expand-on-click-node="multiple"
      :filter-node-method="filterNode"
      @node-click="handleNodeClick"
      @check-change="handleCheckChange"
      :lazy="lazy"
      :load="loadOptions"
    >
        <span style="font-size:14px;" :style="node.disabled ? 'color: #a7a7a7;cursor: not-allowed;width:100%;' : ''" slot-scope="{ node, data }">{{ node[props.label] }}</span>
    </el-tree>
  </el-select>
</template>

<script>
export default {
  name: 'TreeSelect',
  props: {
    // v-model绑定
    value: {
      type: [String, Number, Array],
      default: ''
    },
    multiple: {
      type: Boolean,
      default: false
    },
    // 选择框默认文字
    placeholder: {
      type: String,
      default: '请选择'
    },
    // 树形的数据
    data: {
      type: Array,
      default: function () {
        return []
      }
    },
    // 每个树节点用来作为唯一标识的属性
    nodeKey: {
      type: [String, Number],
      default: 'id'
    },
    filterable: {
      type: Boolean,
      default: false
    },
    disabled: {
      type: Boolean,
      default: false
    },
    // tree的props配置
    props: {
      type: Object,
      default: function () {
        return {
          id: 'id',
          label: 'label',
          children: 'children',
          isLeaf: 'isLeaf'
        }
      }
    },
    // 远程搜索
    remoteSearch: {
      type: Boolean,
      default: false
    },
    // 远程搜索方法
    remoteSearchFunc: {
      type: Function,
      default: undefined
    },
    // 只能选叶子
    onlyLeaf: {
      type: Boolean,
      default: false
    },
    // 懒加载开关
    lazy: {
      type: Boolean,
      default: false
    },
    // 懒加载方法
    lazyFunc: {
      type: Function,
      default: undefined
    },
    // 初始化选项数据
    initOptions: {
      type: Array,
      default: undefined
    }
  },
  data() {
    return {
      selectedData: [{id: -2}],
      searchVal: '',
      remoteData: [],
    }
  },
  watch: {
    value: {
      handler(val) {
        if (this.data && this.data.length) {
          this.init(val)
        }
      },
      immediate: true
    },
    data: function (val) {
      if (val && val.length) {
        this.setDisabled(val)
        this.init(this.value)
      }
    },
    searchVal(val) {
      this.filterMethod(val)
    },
    initOptions: {
      handler(val) {
        if (val && val.length) {
          val.forEach(e => {
            if (!this.selectedData.find(it => it[this.nodeKey] == e[this.nodeKey])) {
              this.selectedData.push(this.deepCopy(e))
            }
          })
        }
      },
      immediate: true
    }
  },
  mounted() {
    if (!this.value) {
      this.$emit('input', this.multiple ? [] : '')
    }
  },
  methods: {
    visibleChange(flag) {
      if (!flag) {
        this.searchVal = ''
      }
    },
    handleNodeClick(data) {
      if (this.multiple || data.disabled) {
        return
      }
      if (this.selectedData.findIndex(e => e[this.nodeKey] == data[this.nodeKey]) == -1) {
        this.selectedData.push(data)
      }
      this.$emit('input', data[this.nodeKey])
      this.$emit('select', data)
      this.$refs.select.visible = false
    },
    handleCheckChange(data, flag) {
      const value = this.value ? this.deepCopy(this.value) : []
      if (flag) {
        if (value.findIndex(e => e == data[this.nodeKey]) == -1) {
          value.push(data[this.nodeKey])
        }
        if (this.selectedData.findIndex(e => e[this.nodeKey] == data[this.nodeKey]) == -1) {
          this.selectedData.push(data)
        }
      } else {
        const idx = value.findIndex(e => e == data[this.nodeKey])
        if (idx != -1) {
          value.splice(idx, 1)
        }
      }
      this.$emit('input', value)
      this.$emit('select', data)
    },
    init(val) {
      // 多选
      if (this.multiple) {
        const arr = val ? val : []
        this.$nextTick(() => {
          this.$refs.tree.setCheckedKeys(arr)
          this.$refs.remoteTree.setCheckedKeys(arr)
        })
      }
      // 单选
      else {
        val = val === '' ? null : val
          this.$nextTick(() => {
            this.$refs.tree.setCurrentKey(val)
            this.$refs.remoteTree.setCurrentKey(val)
          })
      }
    },
    clear() {
      this.$emit('input', '')
    },
    async filterMethod(val) {
      if (this.remoteSearch && val) {
        if (!this.remoteSearchFunc) {
          this.$message.error("远程搜索方法未传入");
          return
        }
        const res = await this.remoteSearchFunc(val)
        this.setDisabled(res)
        this.remoteData = res
        return
      }
      this.remoteData = []
      this.$refs.tree.filter(val)
    },
    filterNode(value, data) {
      if (!value) return true
      const label = this.props.label || 'label'
      return data[label].indexOf(value) !== -1
    },
    removeTag(id) {
      this.$refs.tree.setChecked(id, false)
      this.$refs.remoteTree.setChecked(id, false)
      const idx = this.value.findIndex(e => e == id)
      if (idx != -1) {
        const value = this.deepCopy(this.value)
        value.splice(idx, 1)
        this.$emit('input', value)
      }
    },
    deepCopy(data) {
      return JSON.parse(JSON.stringify(data))
    },
    async loadOptions(node, resolve) {
      if (!this.lazyFunc) {
        this.$message.error("懒加载方法未传入");
        return
      }
      const arr = await this.lazyFunc(node)
      if (arr && arr.length) {
        this.setDisabled(arr)
        resolve(arr)
      } else {
        node.data.disabled = false
        resolve([])
      }
    },
    setDisabled(arr) {
      if (this.onlyLeaf) {
        arr.forEach(e => {
          if (!e.isLeaf) {
            e.disabled = true
          }
        })
      }
    }
  }
}
</script>

<style scoped>
.tree-select__option {
  &.el-select-dropdown__item {
    height: auto;
    line-height: 1;
    padding: 0;
    background-color: #fff;
  }
}

.tree-select__tree {
  padding: 4px 20px;
  font-weight: 400;
  &.tree-select__tree--radio {
    .el-tree-node.is-current > .el-tree-node__content {
      color: #409EFF;
      font-weight: 700;
    }
  }
}

.el-select-dropdown__wrap {
  overflow: scroll !important;
}

.el-scrollbar__view,.el-select-dropdown__list {
  max-height: 100%;
  overflow: auto;
}

.isSearch--padding {
  padding-top: 34px;
}

.isSearch--no_padding {
}

.search_input {
  width: 94%;
  margin: 0 3%;
  z-index: 10;
}

.div_search_input {
  background-color: #ffffff;
  padding-top: 6px;
  top: 0;
  width: 100%;
  position: absolute;
  z-index: 10;
}
</style>